﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;


namespace Tank
{
    enum ParticleFadeStyle
    { 
        NONE,
        EXPAND,
        CONTRACT
    }

    class ParticleEmitter
    {
        Vector2 position;
        Vector2 velocity;
        Vector2 acceleration;
        Vector2 direction;
        Color color;
        uint maxNumParticles;
        uint numToEmit;
        float drag;
        float angVel;
        float angRange;
        float accelMag;
        float acclRange;
        float emitterLifetime;
        float currLifetime;
        float lifeTime;
        float alphaBlendTime;
        float size;
        float startSize, endSize;
        float emitRate;
        float emitTime;
        string textureID;
        bool emit;
        bool emitOnce;
        bool stopEmitting;
        float lastAnglDir;
        Rectangle sourceRect;
        Matrix rotMtx = new Matrix();

        List<Particle> particleList;
        ParticleFadeStyle fadeStyle;

        public ParticleEmitter(uint totNumber, Vector2 pos, Vector2 vel, Vector2 dir, float accel, float drg,
                        float thetaRange, float omega, float deltaAccl, Color clr, float life,
                        float blendTime, float maxSize, float rate, uint numEmit)
        {
            particleList = new List<Particle>();
            position = new Vector2(pos.X, pos.Y);
            velocity = new Vector2(vel.X, vel.Y);
            acceleration = new Vector2(0, 0);
            direction = new Vector2(dir.X, dir.Y);
            accelMag = accel;
            drag = drg;
            maxNumParticles = totNumber;
            numToEmit = numEmit;
            color = clr;
            angRange = thetaRange;
            angVel = omega;
            acclRange = deltaAccl;
            lifeTime = life;
            alphaBlendTime = blendTime;
            size = startSize = endSize = maxSize;
            emitRate = rate;
            emitTime = 0;
            emit = true;
            emitOnce = false;
            stopEmitting = false;
            lastAnglDir = 1.0f;
            fadeStyle = ParticleFadeStyle.NONE;
            emitterLifetime = -1;
            currLifetime = 0;
        }

        public void SetTexture(string texID, Rectangle srcRect)
        {
            textureID = texID;
            sourceRect = srcRect;
        }

        public void SetFadeStyle(ParticleFadeStyle style)
        {
            fadeStyle = style;
        }

        public void SetFadeSize(float start, float end)
        {
            Debug.Assert(start >= 0);
            Debug.Assert(end >= 0);
            startSize = start;
            endSize = end;
        }

        public void Update(float deltaTime)
        {
            // Update emitter life
            currLifetime += deltaTime;

            if (emitterLifetime != -1 && currLifetime >= emitterLifetime)
            { 
                // Stop this emitter
                stopEmitting = true;
            }

            // Update position
            position += velocity * deltaTime;

            emitTime += deltaTime;
            if (emitTime > emitRate && !stopEmitting)
            {
                bool didEmit = Emit(numToEmit);
                if (didEmit && emitOnce)
                    emit = false;                
            }

            for (int i = 0; i < particleList.Count(); ++i)
            {
                if (particleList[i].Remove)
                {
                    particleList.RemoveAt(i);
                    continue;
                }
                particleList[i].Update(deltaTime);
            }
        }

        private bool Emit(uint numToEmit)
        {
            for (int i = 0; i < numToEmit; ++i)
            {
                if (!emit || particleList.Count() >= maxNumParticles)
                {
                    if(i > 0) 
                        return true;
                    return false;
                }
                bool create = CreateParticle(i);
                if (create)
                {
                    emitTime = 0;
                }
            }
            if (numToEmit > 0)
                return true;
            return false;
        }

        private bool CreateParticle(int seed)
        {
            Vector2 dir = VectorFromDirection(direction, angRange, seed);
            dir.Normalize();
            dir *= accelMag;
            Particle p = new Particle(position, new Vector2(), dir, color, size, lifeTime, alphaBlendTime, drag, angVel * lastAnglDir, alphaBlendTime, textureID);

            switch (fadeStyle)
            { 
                case ParticleFadeStyle.EXPAND:
                    p.SetFadeSize(startSize, endSize);
                    break;

                case ParticleFadeStyle.CONTRACT:
                    p.SetFadeSize(startSize, endSize);
                    break;

                default:
                    break;
            }
            particleList.Add(p);
            lastAnglDir *= -1;
            
            return true;
        }

        private Vector2 VectorFromDirection(Vector2 dir, float deltaAngle, int seed)
        {
            Random rand = new Random(seed);
            float angle = (float)rand.Next(-100, 100);  // Get a value between -1 and 1 from and integer
            angle *= .01f * deltaAngle;
            rotMtx = Matrix.CreateRotationZ(angle);
            Vector3 newDirection = new Vector3(direction, 0);
            newDirection = Vector3.Transform(newDirection, rotMtx);
            newDirection.Normalize();
            return new Vector2(newDirection.X, newDirection.Y);
        }

        public void Draw(SpriteBatch spriteBatch)
        {
            foreach(Particle particle in particleList)
            {
                particle.Draw(spriteBatch, sourceRect);
            }
        }

        public string TextureID
        {
            set { textureID = value; }
        }

        public Vector2 Position
        {
            set { position = value; }
        }

        public bool EmitParticles
        {
            set { emit = value; }
        }

        public float EmitterLifeTime
        {
            set { emitterLifetime = value; }
        }

        public void EmitOnce()
        {
            emitOnce = true;
            emit = true;
        }

        public Vector2 Direction
        {
            set { direction = value; }
        }

        public Vector2 Velocity
        {
            set { velocity = value; }
        }
    }
}
